#include "util.hpp"
#include "log.hpp"
#include "ThreadPool.hpp"
#include "task.hpp"

class ServerTcp;

void execCommand(int sock, const std::string &clientIp, uint16_t clientPort)
{
    assert(sock >= 0);
    assert(!clientIp.empty());
    assert(clientPort >= 1024);

    char command[BUFFER_SIZE];
    while (true)
    {
        ssize_t s = read(sock, command, sizeof(command) - 1);
        if (s > 0)
        {
            command[s] = '\0';
            logMessage(DEBUG, "[%s:%d] exec [%s]", clientIp.c_str(), clientPort, command);
            // 考虑安全
            std::string safe = command;
            if ((std::string::npos != safe.find("rm")) || (std::string::npos != safe.find("unlink")))
            {
                break;
            }

            FILE *fp = popen(command, "r");
            if (fp == nullptr)
            {
                logMessage(WARINING, "exec %s failed,because: %s", command, strerror(errno));
                break;
            }

            char line[1024];
            while (fgets(line, sizeof(line) - 1, fp) != nullptr)
            {
                write(sock, line, strlen(line));
            }

            // 我们是以r方式打开的文件，没有写入
            // 所以我们无法通过dup的方式得到对应的结果
            // dup2(sock,fp->_fileno);
            // fflush(fp);
            // pclose(fp);
            logMessage(DEBUG, "[%s:%d] exec [%s] ... done", clientIp.c_str(), clientPort, command);
        }
        else if (s == 0)
        {
            logMessage(DEBUG, "client quit -- %s[%d]", clientIp.c_str(), clientPort);
            break;
        }
        else
        {
            logMessage(DEBUG, "%s[%d] - read:%s", clientIp.c_str(), clientPort, strerror(errno));
            break;
        }
    }
    // 只要走到这里，一定是client退出了，服务到此结束
    close(sock); // 如果一个进程对应的文件fd，打开了没有被归还，文件描述符泄漏！
    logMessage(DEBUG, "server close %d done", sock);
}

// 字符串中小写字符转换为大写字符的服务
void transService(int socket, const std::string &clientIp, uint16_t clientPort)
{
    assert(socket >= 0);
    assert(!clientIp.empty());
    assert(clientPort >= 1024);

#define BUFFER_SIZE 1024
    char inbuffer[BUFFER_SIZE];
    while (true)
    {
        ssize_t s = read(socket, inbuffer, sizeof(inbuffer) - 1);
        if (s > 0)
        {
            inbuffer[s] = '\0';
            // 将小写字符转换为大写字符
            for (int i = 0; i < s; i++)
            {
                if (isalpha(inbuffer[i]) && islower(inbuffer[i]))
                    inbuffer[i] = toupper(inbuffer[i]);
            }
            write(socket, inbuffer, strlen(inbuffer));
        }
        else if (s == 0)
        {
            std::cout << "client quit -- " << clientIp.c_str() << "->" << clientPort << std::endl;
            break;
        }
        else
        {
            std::cout << "read failed : " << strerror(errno) << " " << clientIp.c_str() << "->" << clientPort << std::endl;
            break;
        }
    }
    // 程序执行到此处时，一定是客户端退出了，服务结束。
    close(socket);
}

class ThreadData
{
public:
    uint16_t clientPort_;
    std::string clientIp_;
    int sock_;
    ServerTcp *this_;

    ThreadData(uint16_t port, std::string ip, int sock, ServerTcp *ts)
        : clientPort_(port), clientIp_(ip), sock_(sock), this_(ts)
    {
    }
};

class ServerTcp
{
public:
    ServerTcp(uint16_t port, const std::string &ip = "") : port_(port), ip_(ip), listenSocket_(-1), tp_(nullptr) {}

    ~ServerTcp()
    {
        if (listenSocket_ > 0)
            close(listenSocket_);
    }

    void init()
    {
        // 1.创建socket
        listenSocket_ = socket(PF_INET, SOCK_STREAM, 0);
        if (listenSocket_ < 0)
        {
            std::cerr << "socket create failed : " << strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }

        // 2.bind
        // 2.1 填充服务器信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = PF_INET;
        local.sin_port = htons(port_);
        ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));
        // 2.2 本地socket信息，写入sock_对应的内核区域
        if (bind(listenSocket_, (const struct sockaddr *)&local, sizeof local) < 0)
        {
            std::cerr << "bind failed : " << strerror(errno) << std::endl;
            exit(BIND_ERR);
        }

        // 3.监听socket，为何要监听呢？tcp是面向连接的！
        if (listen(listenSocket_, 5) < 0)
        {
            std::cerr << "listen failed : " << strerror(errno) << std::endl;
            exit(LISTEN_ERR);
        }

        // 4.加载线程池
        tp_ = ThreadPool<Task>::getInstance();
    }

    void loop()
    {
        tp_->start();
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);

            // 4.获取连接，accept的返回值是一个新的socket fd
            int serviceSock = accept(listenSocket_, (struct sockaddr *)&peer, &len);
            if (serviceSock < 0)
            {
                std::cerr << "accept failed : " << strerror(errno) << std::endl;
                continue;
            }
            // 4.1 获取客户端基本信息
            uint16_t peerPort = ntohs(peer.sin_port);
            std::string peerIp = inet_ntoa(peer.sin_addr);

            // 线程池版本
            Task t(serviceSock, peerIp, peerPort, transService);
            tp_->push(t);
        }
    }

private:
    int listenSocket_;
    uint16_t port_;
    std::string ip_;
    ThreadPool<Task> *tp_;
};

static void Usage(std::string proc)
{
    std::cerr << "Usage:\n\t" << proc << " prot ip" << std::endl;
    std::cerr << "Usage:\n\t" << proc << " 8080 127.0.0.1\n"
              << std::endl;
}

// ./serverTcp local_port local_ip
int main(int argc, char *argv[])
{
    if (argc != 2 && argc != 3)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    uint16_t port = atoi(argv[1]);
    std::string ip;
    if (argc == 3)
        ip = argv[2];

    ServerTcp svr(port, ip);
    svr.init();
    svr.loop();
    return 0;
}